package Olivia.olivia3d.render
{
	import Olivia.manager.ResManager;
	import Olivia.olivia3d.Olivia3D;
	import Olivia.olivia3d.camera.OCamera;
	import Olivia.olivia3d.light.OLight;
	import Olivia.olivia3d.model.ms3d.MsGroup;
	import Olivia.olivia3d.model.ms3d.MsMaterial;
	import Olivia.olivia3d.model.ms3d.TempSimplifyModel;
	import Olivia.olivia3d.pick.PickModel;
	import Olivia.olivia3d.shader.FragmentShader;
	import Olivia.olivia3d.shader.VertexShader;
	import Olivia.olivia3d.shadow.ShadowMap;
	import Olivia.olivia3d.vo.Ms3dVo;
	import Olivia.olivia3d.vo.Stage3DVo;
	
	import com.adobe.utils.AGALMiniAssembler;
	
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.display3D.textures.TextureBase;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import flash.ui.Keyboard;
	
	public class RenderManager extends EventDispatcher
	{
		private static var _self:RenderManager;
		public static function get ins():RenderManager
		{
			if(!_self){
				_self = new RenderManager();
			}
			return _self;
		}
		
		private var _main:Sprite;
		private var _camera:OCamera;
		
		public var renders:Array;
		protected var pkm:int;
		protected var kmsidx:int;
		protected var kms3d:Ms3dVo;
		
		public function RenderManager()
		{
			
		}
		
		public function init(c:OCamera):void
		{
			renders = new Array();
			_camera = c;
			_main = Olivia3D.ins.main;
			_main.addEventListener(Event.ENTER_FRAME, setupScene);
			_main.stage.addEventListener(MouseEvent.CLICK,chooseMs3d);
			_main.stage.addEventListener(KeyboardEvent.KEY_DOWN,addKeyBoard);			
		}
		
		protected function chooseMs3d(event:MouseEvent):void
		{
			// TODO Auto-generated method stub
			kmsidx = pkm;			
		}
		
		protected function addKeyBoard(e:KeyboardEvent):void
		{
			// TODO Auto-generated method stub
			if(!kms3d)
				return;
			switch(e.keyCode){
				case Keyboard.Q:
					kms3d.position.w += 1;
					break;				
				case Keyboard.E:
					kms3d.position.w -= 1;
					break;
				case Keyboard.W:
					kms3d.position.z += 1;
					break;
				case Keyboard.S:
					kms3d.position.z -= 1;
					break;
				case Keyboard.A:
					kms3d.position.x += 1;
					break;
				case Keyboard.D:
					kms3d.position.x -= 1;
					break;
			}
		}
		
		//add models in render
		public function addModelInRender(stgIdx:int,msvo:Ms3dVo):void
		{
			var stage3dvo:Stage3DVo;
			var renders:Array = renders;
			if(!renders[stgIdx]){
				stage3dvo = new Stage3DVo();
				stage3dvo.init(stgIdx);
				renders[stgIdx] = stage3dvo;
			}else{
				stage3dvo = renders[stgIdx];
			}
			stage3dvo.models.push(msvo);			
		}
				
		protected function setupScene(event:Event):void
		{			
			//clear all render
			clearRenders();
			//render
			doRender();
			//present all render
			presentRenders();
		}
		
		private function doRender():void
		{
			// TODO Auto Generated method stub
			for each(var s3dvo:Stage3DVo in renders){
				if(!s3dvo.context3D)
					continue;
				//pick model
				pkm = PickModel.ins.getPickMidx(s3dvo);
				//shadow map
				var shadowTexture:Texture = ShadowMap.ins.creatShadowDepth(s3dvo);
//				return;
				s3dvo.context3D.setRenderToBackBuffer();
				s3dvo.context3D.clear(.3,.3,.3,1);								
				//render each model
				for each(var msvo:Ms3dVo in s3dvo.models){
					if(!msvo.initGroupIdx){						
						//create model indexbuffer
						TempSimplifyModel.createMs3dIndexBuffer(s3dvo,msvo);
						msvo.initProgram3D(s3dvo.context3D);
					}
					//0,over;1,out;2,pick;
					var t:int = 0;
					if(kmsidx && msvo.msid==kmsidx){
						kms3d = msvo;
						t = 2;
					}else if(msvo.msid==pkm && pkm!=0){
						t = 1;
					}else if(kmsidx == 0){
						kms3d = null;
					}
					//clear vertex and texture
					s3dvo.clearBufferAndTexture();
					//set vc 顶点着色器
					var idx:int = setVc(s3dvo, msvo);
					//set fc 像素着色器
					setFc(s3dvo,t);
					//set agal
					setAgal(s3dvo,msvo);
					//render model
					renderModel(s3dvo,msvo,shadowTexture,idx);
				}
			}
		}
		
		private function setVc(s3dvo:Stage3DVo, msvo:Ms3dVo):int
		{
			// TODO Auto Generated method stub
			//vc0, world&view&proj
			s3dvo.context3D.setProgramConstantsFromMatrix(
				Context3DProgramType.VERTEX, 0, _camera.getWorldViewProjMat(msvo.position), true);
			//vc4, world
			s3dvo.context3D.setProgramConstantsFromMatrix(
				Context3DProgramType.VERTEX, 4, _camera.getWorld(msvo.position), true);
			//vc8, view
			s3dvo.context3D.setProgramConstantsFromMatrix(
				Context3DProgramType.VERTEX, 8, _camera.view, true);
			var invmat:Matrix3D = _camera.getWorld(msvo.position).clone();
			invmat.invert();
			invmat.transpose();
			//vc12, normal change mat
			s3dvo.context3D.setProgramConstantsFromMatrix(
				Context3DProgramType.VERTEX, 12, invmat, true);		
			//vc16, lighting_world&view&proj
			s3dvo.context3D.setProgramConstantsFromMatrix(
				Context3DProgramType.VERTEX,
				16,
				OLight(s3dvo.lights[0]).camera.getWorldViewProjMat(msvo.position),	//now this is only one light
				true);
			//vc20.xy for uv....vc20.zw for ani offset
			s3dvo.context3D.setProgramConstantsFromVector(
				Context3DProgramType.VERTEX, 
				20,
				Vector.<Number>([
					0.5,
					-0.5,
					1,
					2
				]));
			//vc21->> is joints matrix
			s3dvo.context3D.setProgramConstantsFromVector(
				Context3DProgramType.VERTEX,
				21,
				msvo.getJointDataThisFrame());
			return 21;
		}
		
		private function setFc(s3dvo:Stage3DVo,t:int):void
		{
			// TODO Auto Generated method stub			
			var viewer:Vector.<Number> = Vector.<Number>([_camera.view.position.x,
				_camera.view.position.y,
				_camera.view.position.z,
				_camera.view.position.w]);
			//fc4, viewer position
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,4,viewer);
			
			//fc5, scene lighting direction----temp lighting
//			var tempScene:Vector3D = new Vector3D(-1,1,0,1);
			var tempScene:Vector3D = new Vector3D(
				OLight(s3dvo.lights[0]).camera.viewEye.x - OLight(s3dvo.lights[0]).camera.viewAt.x,
				OLight(s3dvo.lights[0]).camera.viewEye.y - OLight(s3dvo.lights[0]).camera.viewAt.y,
				OLight(s3dvo.lights[0]).camera.viewEye.z - OLight(s3dvo.lights[0]).camera.viewAt.z,
				OLight(s3dvo.lights[0]).camera.viewEye.w - OLight(s3dvo.lights[0]).camera.viewAt.w
			);
			tempScene.normalize();
			tempScene.negate();
			var sceneLight:Vector.<Number> = Vector.<Number>([tempScene.x,tempScene.y,tempScene.z,tempScene.w]);
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,5,sceneLight);
			//fc6, scene lighting color & intensity = [1,1,1,1] so not deal
//			renderContext.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,6,Vector.<Number>([1,1,1,1]));
			
			//other lights
//			otherLights();	
			//deal shadow--------------------------------------------
			//smooth in shadow
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 7, 
				Vector.<Number>([
					0.85,
					ShadowMap.ins.tsize,
					1/ShadowMap.ins.tsize,
					-0.0001]));
			//bitShifts
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 8, 
				Vector.<Number>([
					1.0,
					1/255.0,
					1/65025.0,
					1/16581375.0]));
			//pick model
			if(t == 2){
				s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 9, 
					Vector.<Number>([1,5,1,1]));				
			}else if(t == 1){
				s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 9, 
					Vector.<Number>([1,1,5,1]));
			}else if(t == 0){
				s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 9, 
					Vector.<Number>([1,1,1,1]));
			}	
		}
		
		private function setAgal(s3dvo:Stage3DVo,msvo:Ms3dVo):void
		{
			// TODO Auto Generated method stub
			s3dvo.setAgal(msvo.pro3D);
//			if(msvo.type == Ms3dVo.BaseType){
//				s3dvo.setAgal(VertexShader.withShadowVsAgal(), FragmentShader.getFsAgal());
//			}else if(msvo.type == Ms3dVo.AnimType){
//				s3dvo.setAgal(VertexShader.withShadowVsAgalAni(), FragmentShader.getFsAgal());
//			}
		}
		
		private function renderModel(s3dvo:Stage3DVo,msvo:Ms3dVo,shadowText:Texture,vcidx:int):void
		{
			// TODO Auto Generated method stub
			//vertexes
			var vertexes:VertexBuffer3D = 
				s3dvo.context3D.createVertexBuffer(msvo.vertexData.length/msvo.dataPerVertex, 
					msvo.dataPerVertex);
			vertexes.uploadFromVector(msvo.vertexData,
				0,
				msvo.vertexData.length/msvo.dataPerVertex);
			//normals
			var normals:VertexBuffer3D = s3dvo.context3D.createVertexBuffer(msvo.normalData.length/3, 3);
			normals.uploadFromVector(msvo.normalData,0,msvo.normalData.length/3);
			
			//va0 is position
			s3dvo.context3D.setVertexBufferAt(0, vertexes, 0, Context3DVertexBufferFormat.FLOAT_3); 
			//va1 is normals
			s3dvo.context3D.setVertexBufferAt(1, normals, 0, Context3DVertexBufferFormat.FLOAT_3);
			if(msvo.type == Ms3dVo.BaseType){
				//va2 is uv
				s3dvo.context3D.setVertexBufferAt(2, vertexes, 3, Context3DVertexBufferFormat.FLOAT_2);
			}else if(msvo.type == Ms3dVo.AnimType){			
				//va2 is uv
				s3dvo.context3D.setVertexBufferAt(2, vertexes, 4, Context3DVertexBufferFormat.FLOAT_2);				
				//boneid
				var bonesVector:Vector.<Number> = msvo.getBoneVector(vcidx);
				var bones:VertexBuffer3D = s3dvo.context3D.createVertexBuffer(bonesVector.length/4,4);
				bones.uploadFromVector(
					bonesVector,
					0,
					bonesVector.length/4);
				//va3 is boneid
				s3dvo.context3D.setVertexBufferAt(3, bones, 0, Context3DVertexBufferFormat.FLOAT_4);
			}
			
			//fs0 is shadowTexture
			s3dvo.context3D.setTextureAt(0, shadowText);
			
			for each(var g:MsGroup in msvo.groupData){
				var maIdx:int = g.materialIndex;
				var ma:MsMaterial = msvo.materialData[maIdx];
				//trianles indexList
//				var indexList:IndexBuffer3D = s3dvo.context3D.createIndexBuffer(msvo.triangleData.length);
//				indexList.uploadFromVector(msvo.triangleData,0,msvo.triangleData.length);
				renderGroup(s3dvo, g, ma, g.indexBuffer);
			}
		}
		
		private function renderGroup(s3dvo:Stage3DVo,g:MsGroup,ma:MsMaterial,idxList:IndexBuffer3D):void
		{
			var ambient:Vector.<Number> = Vector.<Number>(ma.ambient);
			//fc0, ambient lighting
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,0,ambient);
			
			var diffuse:Vector.<Number> = Vector.<Number>(ma.diffuse);
			//fc1, diffuse lighting
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,1,diffuse);
			
			var specular:Vector.<Number> = Vector.<Number>(ma.specular);
			//fc2, specular lighting
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,2,specular);
			
			var emissive:Vector.<Number> = Vector.<Number>(ma.emissive);
			//fc3, emissive lighting
			s3dvo.context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,3,emissive);
			
			//ma.shininess,ma.transparency,ma.mode,ma.alphamap... don't deal
			
			var texture:TextureBase = s3dvo.getTexture(ma.texture);
			//fs1 model's group is bmp
			s3dvo.context3D.setTextureAt(1,texture);
			
			//dragTriangles
			s3dvo.context3D.drawTriangles(idxList);
		}
		
		protected function clearRenders():void
		{
			for each(var stage3DVo:Stage3DVo in renders)
			{
				if(!stage3DVo.stage3D.context3D)
					continue;
				stage3DVo.stage3D.context3D.clear(.3,.3,.3,1);
			}
		}
		
		protected function presentRenders():void
		{
			for each(var stage3DVo:Stage3DVo in renders)
			{
				if(!stage3DVo.stage3D.context3D)
					continue;
				stage3DVo.stage3D.context3D.present();
			}
		}
	}
}